// Copyright 2008 Google Inc.
// All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Author: vladl@google.com (Vlad Losev)

// Type and function utilities for implementing parameterized tests.

#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_
#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_

#include <iterator>
#include <utility>
#include <vector>

// scripts/fuse_gtest.py depends on gtest's own header being #included
// *unconditionally*.  Therefore these #includes cannot be moved
// inside #if GTEST_HAS_PARAM_TEST.
#include "gtest/internal/gtest-internal.h"
#include "gtest/internal/gtest-linked_ptr.h"
#include "gtest/internal/gtest-port.h"
#include "gtest/gtest-printers.h"

#if GTEST_HAS_PARAM_TEST

namespace testing
{
	namespace internal
	{

		// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
		//
		// Outputs a message explaining invalid registration of different
		// fixture class for the same test case. This may happen when
		// TEST_P macro is used to define two tests with the same name
		// but in different namespaces.
		GTEST_API_ void ReportInvalidTestCaseType(const char *test_case_name,
		                                          const char *file, int line);

		template <typename> class ParamGeneratorInterface;
		template <typename> class ParamGenerator;

		// Interface for iterating over elements provided by an implementation
		// of ParamGeneratorInterface<T>.
		template <typename T>
		class ParamIteratorInterface
		{
		public:
			virtual ~ParamIteratorInterface() {}
			// A pointer to the base generator instance.
			// Used only for the purposes of iterator comparison
			// to make sure that two iterators belong to the same generator.
			virtual const ParamGeneratorInterface<T> *BaseGenerator() const = 0;
			// Advances iterator to point to the next element
			// provided by the generator. The caller is responsible
			// for not calling Advance() on an iterator equal to
			// BaseGenerator()->End().
			virtual void Advance() = 0;
			// Clones the iterator object. Used for implementing copy semantics
			// of ParamIterator<T>.
			virtual ParamIteratorInterface *Clone() const = 0;
			// Dereferences the current iterator and provides (read-only) access
			// to the pointed value. It is the caller's responsibility not to call
			// Current() on an iterator equal to BaseGenerator()->End().
			// Used for implementing ParamGenerator<T>::operator*().
			virtual const T *Current() const = 0;
			// Determines whether the given iterator and other point to the same
			// element in the sequence generated by the generator.
			// Used for implementing ParamGenerator<T>::operator==().
			virtual bool Equals(const ParamIteratorInterface &other) const = 0;
		};

		// Class iterating over elements provided by an implementation of
		// ParamGeneratorInterface<T>. It wraps ParamIteratorInterface<T>
		// and implements the const forward iterator concept.
		template <typename T>
		class ParamIterator
		{
		public:
			typedef T value_type;
			typedef const T &reference;
			typedef ptrdiff_t difference_type;

			// ParamIterator assumes ownership of the impl_ pointer.
			ParamIterator(const ParamIterator &other) : impl_(other.impl_->Clone()) {}
			ParamIterator &operator=(const ParamIterator &other)
			{
				if (this != &other)
					impl_.reset(other.impl_->Clone());
				return *this;
			}

			const T &operator*() const
			{
				return *impl_->Current();
			}
			const T *operator->() const
			{
				return impl_->Current();
			}
			// Prefix version of operator++.
			ParamIterator &operator++()
			{
				impl_->Advance();
				return *this;
			}
			// Postfix version of operator++.
			ParamIterator operator++(int /*unused*/)
			{
				ParamIteratorInterface<T> *clone = impl_->Clone();
				impl_->Advance();
				return ParamIterator(clone);
			}
			bool operator==(const ParamIterator &other) const
			{
				return impl_.get() == other.impl_.get() || impl_->Equals(*other.impl_);
			}
			bool operator!=(const ParamIterator &other) const
			{
				return !(*this == other);
			}

		private:
			friend class ParamGenerator<T>;
			explicit ParamIterator(ParamIteratorInterface<T> *impl) : impl_(impl) {}
			scoped_ptr<ParamIteratorInterface<T>> impl_;
		};

		// ParamGeneratorInterface<T> is the binary interface to access generators
		// defined in other translation units.
		template <typename T>
		class ParamGeneratorInterface
		{
		public:
			typedef T ParamType;

			virtual ~ParamGeneratorInterface() {}

			// Generator interface definition
			virtual ParamIteratorInterface<T> *Begin() const = 0;
			virtual ParamIteratorInterface<T> *End() const = 0;
		};

		// Wraps ParamGeneratorInterface<T> and provides general generator syntax
		// compatible with the STL Container concept.
		// This class implements copy initialization semantics and the contained
		// ParamGeneratorInterface<T> instance is shared among all copies
		// of the original object. This is possible because that instance is immutable.
		template<typename T>
		class ParamGenerator
		{
		public:
			typedef ParamIterator<T> iterator;

			explicit ParamGenerator(ParamGeneratorInterface<T> *impl) : impl_(impl) {}
			ParamGenerator(const ParamGenerator &other) : impl_(other.impl_) {}

			ParamGenerator &operator=(const ParamGenerator &other)
			{
				impl_ = other.impl_;
				return *this;
			}

			iterator begin() const
			{
				return iterator(impl_->Begin());
			}
			iterator end() const
			{
				return iterator(impl_->End());
			}

		private:
			linked_ptr<const ParamGeneratorInterface<T>> impl_;
		};

		// Generates values from a range of two comparable values. Can be used to
		// generate sequences of user-defined types that implement operator+() and
		// operator<().
		// This class is used in the Range() function.
		template <typename T, typename IncrementT>
		class RangeGenerator : public ParamGeneratorInterface<T>
		{
		public:
			RangeGenerator(T begin, T end, IncrementT step)
				: begin_(begin), end_(end),
				  step_(step), end_index_(CalculateEndIndex(begin, end, step)) {}
			virtual ~RangeGenerator() {}

			virtual ParamIteratorInterface<T> *Begin() const
			{
				return new Iterator(this, begin_, 0, step_);
			}
			virtual ParamIteratorInterface<T> *End() const
			{
				return new Iterator(this, end_, end_index_, step_);
			}

		private:
			class Iterator : public ParamIteratorInterface<T>
			{
			public:
				Iterator(const ParamGeneratorInterface<T> *base, T value, int index,
				         IncrementT step)
					: base_(base), value_(value), index_(index), step_(step) {}
				virtual ~Iterator() {}

				virtual const ParamGeneratorInterface<T> *BaseGenerator() const
				{
					return base_;
				}
				virtual void Advance()
				{
					value_ = value_ + step_;
					index_++;
				}
				virtual ParamIteratorInterface<T> *Clone() const
				{
					return new Iterator(*this);
				}
				virtual const T *Current() const
				{
					return &value_;
				}
				virtual bool Equals(const ParamIteratorInterface<T> &other) const
				{
					// Having the same base generator guarantees that the other
					// iterator is of the same type and we can downcast.
					GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
					        << "The program attempted to compare iterators "
					        << "from different generators." << std::endl;
					const int other_index =
					    CheckedDowncastToActualType<const Iterator>(&other)->index_;
					return index_ == other_index;
				}

			private:
				Iterator(const Iterator &other)
					: ParamIteratorInterface<T>(),
					  base_(other.base_), value_(other.value_), index_(other.index_),
					  step_(other.step_) {}

				// No implementation - assignment is unsupported.
				void operator=(const Iterator &other);

				const ParamGeneratorInterface<T> *const base_;
				T value_;
				int index_;
				const IncrementT step_;
			};  // class RangeGenerator::Iterator

			static int CalculateEndIndex(const T &begin,
			                             const T &end,
			                             const IncrementT &step)
			{
				int end_index = 0;
				for (T i = begin; i < end; i = i + step)
					end_index++;
				return end_index;
			}

			// No implementation - assignment is unsupported.
			void operator=(const RangeGenerator &other);

			const T begin_;
			const T end_;
			const IncrementT step_;
			// The index for the end() iterator. All the elements in the generated
			// sequence are indexed (0-based) to aid iterator comparison.
			const int end_index_;
		};  // class RangeGenerator


		// Generates values from a pair of STL-style iterators. Used in the
		// ValuesIn() function. The elements are copied from the source range
		// since the source can be located on the stack, and the generator
		// is likely to persist beyond that stack frame.
		template <typename T>
		class ValuesInIteratorRangeGenerator : public ParamGeneratorInterface<T>
		{
		public:
			template <typename ForwardIterator>
			ValuesInIteratorRangeGenerator(ForwardIterator begin, ForwardIterator end)
				: container_(begin, end) {}
			virtual ~ValuesInIteratorRangeGenerator() {}

			virtual ParamIteratorInterface<T> *Begin() const
			{
				return new Iterator(this, container_.begin());
			}
			virtual ParamIteratorInterface<T> *End() const
			{
				return new Iterator(this, container_.end());
			}

		private:
			typedef typename ::std::vector<T> ContainerType;

			class Iterator : public ParamIteratorInterface<T>
			{
			public:
				Iterator(const ParamGeneratorInterface<T> *base,
				         typename ContainerType::const_iterator iterator)
					: base_(base), iterator_(iterator) {}
				virtual ~Iterator() {}

				virtual const ParamGeneratorInterface<T> *BaseGenerator() const
				{
					return base_;
				}
				virtual void Advance()
				{
					++iterator_;
					value_.reset();
				}
				virtual ParamIteratorInterface<T> *Clone() const
				{
					return new Iterator(*this);
				}
				// We need to use cached value referenced by iterator_ because *iterator_
				// can return a temporary object (and of type other then T), so just
				// having "return &*iterator_;" doesn't work.
				// value_ is updated here and not in Advance() because Advance()
				// can advance iterator_ beyond the end of the range, and we cannot
				// detect that fact. The client code, on the other hand, is
				// responsible for not calling Current() on an out-of-range iterator.
				virtual const T *Current() const
				{
					if (value_.get() == NULL)
						value_.reset(new T(*iterator_));
					return value_.get();
				}
				virtual bool Equals(const ParamIteratorInterface<T> &other) const
				{
					// Having the same base generator guarantees that the other
					// iterator is of the same type and we can downcast.
					GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
					        << "The program attempted to compare iterators "
					        << "from different generators." << std::endl;
					return iterator_ ==
					       CheckedDowncastToActualType<const Iterator>(&other)->iterator_;
				}

			private:
				Iterator(const Iterator &other)
				// The explicit constructor call suppresses a false warning
				// emitted by gcc when supplied with the -Wextra option.
					: ParamIteratorInterface<T>(),
					  base_(other.base_),
					  iterator_(other.iterator_) {}

				const ParamGeneratorInterface<T> *const base_;
				typename ContainerType::const_iterator iterator_;
				// A cached value of *iterator_. We keep it here to allow access by
				// pointer in the wrapping iterator's operator->().
				// value_ needs to be mutable to be accessed in Current().
				// Use of scoped_ptr helps manage cached value's lifetime,
				// which is bound by the lifespan of the iterator itself.
				mutable scoped_ptr<const T> value_;
			};  // class ValuesInIteratorRangeGenerator::Iterator

			// No implementation - assignment is unsupported.
			void operator=(const ValuesInIteratorRangeGenerator &other);

			const ContainerType container_;
		};  // class ValuesInIteratorRangeGenerator

		// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
		//
		// Stores a parameter value and later creates tests parameterized with that
		// value.
		template <class TestClass>
		class ParameterizedTestFactory : public TestFactoryBase
		{
		public:
			typedef typename TestClass::ParamType ParamType;
			explicit ParameterizedTestFactory(ParamType parameter) :
				parameter_(parameter) {}
			virtual Test *CreateTest()
			{
				TestClass::SetParam(&parameter_);
				return new TestClass();
			}

		private:
			const ParamType parameter_;

			GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestFactory);
		};

		// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
		//
		// TestMetaFactoryBase is a base class for meta-factories that create
		// test factories for passing into MakeAndRegisterTestInfo function.
		template <class ParamType>
		class TestMetaFactoryBase
		{
		public:
			virtual ~TestMetaFactoryBase() {}

			virtual TestFactoryBase *CreateTestFactory(ParamType parameter) = 0;
		};

		// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
		//
		// TestMetaFactory creates test factories for passing into
		// MakeAndRegisterTestInfo function. Since MakeAndRegisterTestInfo receives
		// ownership of test factory pointer, same factory object cannot be passed
		// into that method twice. But ParameterizedTestCaseInfo is going to call
		// it for each Test/Parameter value combination. Thus it needs meta factory
		// creator class.
		template <class TestCase>
		class TestMetaFactory
			: public TestMetaFactoryBase<typename TestCase::ParamType>
		{
		public:
			typedef typename TestCase::ParamType ParamType;

			TestMetaFactory() {}

			virtual TestFactoryBase *CreateTestFactory(ParamType parameter)
			{
				return new ParameterizedTestFactory<TestCase>(parameter);
			}

		private:
			GTEST_DISALLOW_COPY_AND_ASSIGN_(TestMetaFactory);
		};

		// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
		//
		// ParameterizedTestCaseInfoBase is a generic interface
		// to ParameterizedTestCaseInfo classes. ParameterizedTestCaseInfoBase
		// accumulates test information provided by TEST_P macro invocations
		// and generators provided by INSTANTIATE_TEST_CASE_P macro invocations
		// and uses that information to register all resulting test instances
		// in RegisterTests method. The ParameterizeTestCaseRegistry class holds
		// a collection of pointers to the ParameterizedTestCaseInfo objects
		// and calls RegisterTests() on each of them when asked.
		class ParameterizedTestCaseInfoBase
		{
		public:
			virtual ~ParameterizedTestCaseInfoBase() {}

			// Base part of test case name for display purposes.
			virtual const string &GetTestCaseName() const = 0;
			// Test case id to verify identity.
			virtual TypeId GetTestCaseTypeId() const = 0;
			// UnitTest class invokes this method to register tests in this
			// test case right before running them in RUN_ALL_TESTS macro.
			// This method should not be called more then once on any single
			// instance of a ParameterizedTestCaseInfoBase derived class.
			virtual void RegisterTests() = 0;

		protected:
			ParameterizedTestCaseInfoBase() {}

		private:
			GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseInfoBase);
		};

		// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
		//
		// ParameterizedTestCaseInfo accumulates tests obtained from TEST_P
		// macro invocations for a particular test case and generators
		// obtained from INSTANTIATE_TEST_CASE_P macro invocations for that
		// test case. It registers tests with all values generated by all
		// generators when asked.
		template <class TestCase>
		class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase
		{
		public:
			// ParamType and GeneratorCreationFunc are private types but are required
			// for declarations of public methods AddTestPattern() and
			// AddTestCaseInstantiation().
			typedef typename TestCase::ParamType ParamType;
			// A function that returns an instance of appropriate generator type.
			typedef ParamGenerator<ParamType>(GeneratorCreationFunc)();

			explicit ParameterizedTestCaseInfo(const char *name)
				: test_case_name_(name) {}

			// Test case base name for display purposes.
			virtual const string &GetTestCaseName() const
			{
				return test_case_name_;
			}
			// Test case id to verify identity.
			virtual TypeId GetTestCaseTypeId() const
			{
				return GetTypeId<TestCase>();
			}
			// TEST_P macro uses AddTestPattern() to record information
			// about a single test in a LocalTestInfo structure.
			// test_case_name is the base name of the test case (without invocation
			// prefix). test_base_name is the name of an individual test without
			// parameter index. For the test SequenceA/FooTest.DoBar/1 FooTest is
			// test case base name and DoBar is test base name.
			void AddTestPattern(const char *test_case_name,
			                    const char *test_base_name,
			                    TestMetaFactoryBase<ParamType> *meta_factory)
			{
				tests_.push_back(linked_ptr<TestInfo>(new TestInfo(test_case_name,
				                                                   test_base_name,
				                                                   meta_factory)));
			}
			// INSTANTIATE_TEST_CASE_P macro uses AddGenerator() to record information
			// about a generator.
			int AddTestCaseInstantiation(const string &instantiation_name,
			                             GeneratorCreationFunc *func,
			                             const char * /* file */,
			                             int /* line */)
			{
				instantiations_.push_back(::std::make_pair(instantiation_name, func));
				return 0;  // Return value used only to run this method in namespace scope.
			}
			// UnitTest class invokes this method to register tests in this test case
			// test cases right before running tests in RUN_ALL_TESTS macro.
			// This method should not be called more then once on any single
			// instance of a ParameterizedTestCaseInfoBase derived class.
			// UnitTest has a guard to prevent from calling this method more then once.
			virtual void RegisterTests()
			{
				for (typename TestInfoContainer::iterator test_it = tests_.begin();
				     test_it != tests_.end(); ++test_it)
				{
					linked_ptr<TestInfo> test_info = *test_it;
					for (typename InstantiationContainer::iterator gen_it =
					         instantiations_.begin(); gen_it != instantiations_.end();
					     ++gen_it)
					{
						const string &instantiation_name = gen_it->first;
						ParamGenerator<ParamType> generator((*gen_it->second)());

						string test_case_name;
						if (!instantiation_name.empty())
							test_case_name = instantiation_name + "/";
						test_case_name += test_info->test_case_base_name;

						int i = 0;
						for (typename ParamGenerator<ParamType>::iterator param_it =
						         generator.begin();
						     param_it != generator.end(); ++param_it, ++i)
						{
							Message test_name_stream;
							test_name_stream << test_info->test_base_name << "/" << i;
							MakeAndRegisterTestInfo(
							    test_case_name.c_str(),
							    test_name_stream.GetString().c_str(),
							    NULL,  // No type parameter.
							    PrintToString(*param_it).c_str(),
							    GetTestCaseTypeId(),
							    TestCase::SetUpTestCase,
							    TestCase::TearDownTestCase,
							    test_info->test_meta_factory->CreateTestFactory(*param_it));
						}  // for param_it
					}  // for gen_it
				}  // for test_it
			}  // RegisterTests

		private:
			// LocalTestInfo structure keeps information about a single test registered
			// with TEST_P macro.
			struct TestInfo
			{
				TestInfo(const char *a_test_case_base_name,
				         const char *a_test_base_name,
				         TestMetaFactoryBase<ParamType> *a_test_meta_factory) :
					test_case_base_name(a_test_case_base_name),
					test_base_name(a_test_base_name),
					test_meta_factory(a_test_meta_factory) {}

				const string test_case_base_name;
				const string test_base_name;
				const scoped_ptr<TestMetaFactoryBase<ParamType>> test_meta_factory;
			};
			typedef ::std::vector<linked_ptr<TestInfo>> TestInfoContainer;
			// Keeps pairs of <Instantiation name, Sequence generator creation function>
			// received from INSTANTIATE_TEST_CASE_P macros.
			typedef ::std::vector<std::pair<string, GeneratorCreationFunc *>>
			                                                               InstantiationContainer;

			const string test_case_name_;
			TestInfoContainer tests_;
			InstantiationContainer instantiations_;

			GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseInfo);
		};  // class ParameterizedTestCaseInfo

		// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
		//
		// ParameterizedTestCaseRegistry contains a map of ParameterizedTestCaseInfoBase
		// classes accessed by test case names. TEST_P and INSTANTIATE_TEST_CASE_P
		// macros use it to locate their corresponding ParameterizedTestCaseInfo
		// descriptors.
		class ParameterizedTestCaseRegistry
		{
		public:
			ParameterizedTestCaseRegistry() {}
			~ParameterizedTestCaseRegistry()
			{
				for (TestCaseInfoContainer::iterator it = test_case_infos_.begin();
				     it != test_case_infos_.end(); ++it)
				{
					delete *it;
				}
			}

			// Looks up or creates and returns a structure containing information about
			// tests and instantiations of a particular test case.
			template <class TestCase>
			ParameterizedTestCaseInfo<TestCase> *GetTestCasePatternHolder(
			    const char *test_case_name,
			    const char *file,
			    int line)
			{
				ParameterizedTestCaseInfo<TestCase> *typed_test_info = NULL;
				for (TestCaseInfoContainer::iterator it = test_case_infos_.begin();
				     it != test_case_infos_.end(); ++it)
				{
					if ((*it)->GetTestCaseName() == test_case_name)
					{
						if ((*it)->GetTestCaseTypeId() != GetTypeId<TestCase>())
						{
							// Complain about incorrect usage of Google Test facilities
							// and terminate the program since we cannot guaranty correct
							// test case setup and tear-down in this case.
							ReportInvalidTestCaseType(test_case_name,  file, line);
							posix::Abort();
						}
						else
						{
							// At this point we are sure that the object we found is of the same
							// type we are looking for, so we downcast it to that type
							// without further checks.
							typed_test_info = CheckedDowncastToActualType <
							                  ParameterizedTestCaseInfo<TestCase> > (*it);
						}
						break;
					}
				}
				if (typed_test_info == NULL)
				{
					typed_test_info = new ParameterizedTestCaseInfo<TestCase>(test_case_name);
					test_case_infos_.push_back(typed_test_info);
				}
				return typed_test_info;
			}
			void RegisterTests()
			{
				for (TestCaseInfoContainer::iterator it = test_case_infos_.begin();
				     it != test_case_infos_.end(); ++it)
				{
					(*it)->RegisterTests();
				}
			}

		private:
			typedef ::std::vector<ParameterizedTestCaseInfoBase *> TestCaseInfoContainer;

			TestCaseInfoContainer test_case_infos_;

			GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseRegistry);
		};

	}  // namespace internal
}  // namespace testing

#endif  //  GTEST_HAS_PARAM_TEST

#endif  // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_
